package com.tte.common.utils.file.impl;

import com.tte.common.GlobalException.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

/**
 * 下载工具类
 * @Author DongWH
 */
@Slf4j
@Component
public class DownloadUtils {

   /**
    * 下载方式 1 (适用于下载小文件)
    * 使用工具类方法将文件直接转成字节数组，而后放入response响应流中
    * @date: 2024/11/20 13:49
    */ 
    public static void downloadFile(File file,HttpServletResponse response){
        // 清空response
        response.reset();
        try {
            String filename = file.getName();
            filename = URLEncoder.encode(filename, "UTF-8");
            // 将文件写入输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            //Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
            //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
            // filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
            response.addHeader("Content-Disposition", "attachment;filename=" + filename);
            response.setContentType("application/octet-stream");
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            outputStream.write(buffer);
            outputStream.flush();
        } catch (IOException e) {
            log.error("下载文件失败",e);
            throw new ServiceException("下载文件失败");
        }

    }



    /**
     * 将文件转成输入流，通过循环边读边写方式写入到response输出流中 (可用于大文件)
     * @param file
     * @return
     * @throws IOException
     */
    public static void downloadBigFile(File file,HttpServletResponse response) {
            // 设置响应头信息
            String filename = file.getName();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            // 指定下载文件名(attachment-以下载方式保存到本地，inline-在线预览)
            response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());
            // 内容类型为通用类型，表示二进制数据流
            response.setContentType("application/octet-stream");
            InputStream is = null;
            BufferedOutputStream bos = null;
            try{
                is = new FileInputStream(file);
                BufferedInputStream bis = new BufferedInputStream(is);
                bos = new BufferedOutputStream(response.getOutputStream());
                byte[] buff = new byte[1024];
                int len;
                while ((len = bis.read(buff)) != -1){
                    bos.write(buff, 0, len);
                }
                bos.flush();
            } catch (IOException e){
                log.error("下载文件失败",e);
                throw new ServiceException("下载文件失败");
            }finally {
                IOUtils.closeQuietly(is);
                IOUtils.closeQuietly(bos);
            }
    }


    /**
     * 使用ResponseEntity返回字节数组(适用于下载小文件)
     * @param file
     * @return
     * @throws IOException
     */
    public static ResponseEntity<byte[]> downloadEntity(File file) {
        try {
            // 设置响应头信息
            String filename = file.getName();
            // 文件转成字节数组
            byte[] fileByte = Files.readAllBytes(file.toPath());
            HttpHeaders headers = new HttpHeaders();
            // 指定下载文件名(attachment-以下载方式保存到本地，inline-在线预览)
            // headers.add("Content-Disposition", "inline; filename=\"" + filename + "\"");
            headers.setContentDisposition(ContentDisposition.builder("inline").filename(filename, StandardCharsets.ISO_8859_1).build());
            headers.setContentType(MediaType.parseMediaType("application/octet-stream"));
            headers.setContentLength(file.length());
            // 构建响应实体(可以返回<byte[]或Resource，返回类型取决body入参类型)
            ResponseEntity<byte[]> responseEntity = ResponseEntity.ok().headers(headers).body(fileByte);
            return responseEntity;
        } catch (IOException e) {
            log.error("下载文件失败",e);
            throw new ServiceException("下载文件失败");
        }

    }


    /**
     * 使用ResponseEntity返回字节数组(适用于下载大文件)
     * @param file
     * @return
     * @throws IOException
     */
    public static ResponseEntity<Resource> downloadBigFileEntity(File file) {
        // 设置响应头信息
        String filename = file.getName();
        // 文件转成字节数组
        // 创建资源对象（以绝对路径访问系统资源）
        Resource resource = new FileSystemResource(file);
        HttpHeaders headers = new HttpHeaders();
        // 指定下载文件名(attachment-以下载方式保存到本地，inline-在线预览)
        // headers.add("Content-Disposition", "inline; filename=\"" + filename + "\"");
        headers.setContentDisposition(ContentDisposition.builder("inline").filename(filename, StandardCharsets.ISO_8859_1).build());
        headers.setContentType(MediaType.parseMediaType("application/octet-stream"));
        headers.setContentLength(file.length());

        // 构建响应实体(可以返回<byte[]或Resource，返回类型取决body入参类型)
        ResponseEntity<Resource> responseEntity = ResponseEntity.ok().headers(headers).body(resource);
        return responseEntity;

    }

}